home *** CD-ROM | disk | FTP | other *** search
- /* =========================================================================
-
- Functions for very basic RFC-822 header manipulation
-
- Filename: rfc822.c
- Last Edited: March 7, 1997
- Authors: Laurence Lundblade, Myra Callen, Bob Fronabarger
- Copyright: 1995, 1996 QUALCOMM Inc.
- Technical support: <emsapi-info@qualcomm.com>
-
- Some of this code is from the c-client and is
- Copyright University of Washington
- */
-
- #include <string.h>
- #include "CopyCat.h"
- #include "RFC822.h"
-
-
- /* All these special characters will confuse CodeWarrior so you may not get
- * the colorized comments and key words you are use to.
- */
- extern const char *hSpecials = " ()<>@,;:\\\042"; /* parse-host specials (\042 = ") */
- extern const char *wSpecials = " ()<>@,;:\\\042[]"; /* parse-word specials */
- extern const char *ptSpecials = " ()<>@,;:\\\042[]/?="; /* parse-token specials */
-
- const char *kRFC822_MustBeQuoted = "()<>@,;:\\\042/[]?= ";
- const char *kRFC822_MustBeEscaped = "\\\042";
-
- char *RFC822_UnquoteStrCpy(char *dst, const char *src, unsigned int len);
-
-
- /* =========================================================================
- * Find next RFC822 token in given string, copying it into a newly created
- * string. Advance pointer past token and any following whitespace.
- *
- * NOTE: The user of this function is responsible for freeing returned string.
- *
- * Args: cpp [IN/OUT] Handle (pointer-to-pointer) of RFC822 string to extract from
- *
- * Returns: String containing next token, nil if error.
- * Moves cpp to first non-whitespace character AFTER extracted token
- */
- char *RFC822_ExtractToken(char **cpp)
- {
- char *start, *buf, *cp = *cpp;
- short len;
-
- start = cp = RFC822_SkipWS(cp); // Skip white space
-
- cp = RFC822_SkipWord(cp); // Skip word
- if (cp > start) {
- len = cp - start;
- buf = NewPtr(len + 1);
- if (buf) {
- RFC822_UnquoteStrCpy(buf, start, len);
- *cpp = RFC822_SkipWS(cp);
- return buf;
- }
- }
- return nil; // No token to extract
- }
-
-
- /* =========================================================================
- * Skips RFC822 whitespace
- *
- * Args: Text string
- *
- * Returns: Moves cpp to first non-whitespace character
- */
- char *RFC822_SkipWS(char *cp)
- {
- long nested;
-
- do {
- while (*cp == ' ') // Skip spaces
- cp++;
-
- if (*cp == '(') { // A comment?
- nested = 1;
- while ((*++cp) && nested) { // Find end of comment
- switch (*cp) {
- case '(':
- nested++;
- break;
- case ')':
- nested--;
- break;
- case '\\': // Escape character
- if ((*(cp + 1)) != '\0') // Check for end-of-string
- cp++;
- break;
- case '"': // Quote inside comment
- while (*cp && (*++cp != '"')) // Go 'til end of quote
- if ((*cp == '\\') && ((*(cp + 1)) != '\0'))
- cp++; // Escaped character inside quote -- inside comment (eeek!)
- break;
- }
- }
- }
- } while (*cp == ' ');
- return cp;
- }
-
-
- /* =========================================================================
- * Advances to next character in string directly after the current token.
- * The first character must be the beginning of a valid token or end of
- * string (ie. all whitespace must be skipped BEFORE calling this function).
- *
- * Args: cp [IN] RFC822 string
- *
- * Returns: Pointer to next valid whitespace character.
- */
- char *RFC822_SkipWord(char *cp)
- {
- Boolean bInQuotes = false;
-
- if (*cp == '"') { // First character must be a quote to be valid double-quoted string
- bInQuotes = true;
- cp++;
- }
- while (*cp) {
- if (strchr(kRFC822_MustBeQuoted, *cp)) {
- if (!bInQuotes) // Found a character that should be double-quoted, but is not
- return (cp);
- switch (*cp) { // Now we know we are inside a double-quoted string
- case '"':
- return (cp + 1); // End double-quote
- case '\\':
- cp++; // Escape character, skip next
- break;
- }
- }
- cp++;
- }
- return cp;
- }
-
-
- /* =========================================================================
- * Calculates the length of the given text string if it were converted
- * to an RFC822 string.
- *
- * Args: cp [IN] Text string
- *
- * Returns: Equivalent RFC822 length of given text.
- */
- unsigned short RFC822_QuotedStrLen(StringPtr theStr)
- {
- unsigned short len = 0;
- char s[256], *cp; // i know these strings are short
- Boolean quoted = false;
-
- BlockMoveData(theStr, s, theStr[0] + 1);
- PtoCstr((StringPtr) s);
- cp = s;
- while (*cp) {
- len++;
- if (!quoted && (strchr(kRFC822_MustBeQuoted, *cp) != nil))
- quoted = true;
- if (strchr(kRFC822_MustBeEscaped, *cp) != nil) {
- len++;
- quoted = true;
- }
- cp++;
- }
- if (quoted)
- len += 2; // Quotes
- return len;
- }
-
-
- /* =========================================================================
- * Copies and converts the source text string to a destination
- * RFC822 string. The source must be NULL terminated, and the
- * destination will be NULL terminated.
- *
- * Args: dst [OUT] RFC822 string
- * src [IN] Text string
- *
- * Returns: Pointer to destination NULL termination.
- */
- char *RFC822_QuoteStrCpy(char *dst, StringPtr theStr)
- {
- char *src;
- Boolean quoted;
-
- PtoCstr(theStr);
- src = (char*) theStr;
- quoted = (strpbrk(src, kRFC822_MustBeQuoted) != nil);
- if (quoted)
- *dst++ = '"';
- while (*src) {
- if (strchr(kRFC822_MustBeEscaped, *src) != nil)
- *dst++ = '\\';
- *dst++ = *src++;
- }
- if (quoted)
- *dst++ = '"';
- *dst = '\0';
- CtoPstr((char*) theStr);
- return dst;
- }
-
-
- /* =========================================================================
- * Copies and converts the source RFC822 string to a destination
- * text string. The source must be null terminated, and the
- * destination will be null terminated.
- *
- * Args: dst [OUT] Text string
- * src [IN] RFC822 string
- * len [IN] Maximum charcters to copy; Zero implies whole string
- *
- * Returns: Pointer to destination nil termination.
- */
- char *RFC822_UnquoteStrCpy(char *dst, const char *src, unsigned int len)
- {
- char *end;
- Boolean escaped = false;
-
- end = strchr(src, '\0');
- if ((len > 0) && ((src + len) < end))
- end = (char*) src + len;
- while (src < end) {
- if (escaped)
- escaped = false;
- else {
- switch (*src) {
- case '\\':
- escaped = true;
- case '"':
- src++;
- continue;
- }
- }
- *dst++ = *src++;
- }
- *dst = '\0';
- return dst;
- }
-
-
- /* =========================================================================
- * Finds and extracts a header line from a full multi-lined header. All
- * unfolding (removing newlines) is done before header line is returned.
- *
- * NOTE: The user of this function is responsible for freeing the
- * returned string.
- *
- * Args: pFullHeader [IN] Pointer to a full RFC822 header, including newlines
- * pLinePrefix [IN] Prefix of header line to extract
- *
- * Returns: Extracted header line string; dynamically allocated.
- */
- char *RFC822_ExtractHeader(const char *pFullHeader, const char *pLinePrefix)
- {
- const char *kNewline = "\r\n";
- const unsigned short kNewlineLen = 2;
- const unsigned short nPreLen = strlen(pLinePrefix);
- char *pStart, *pEnd, *pBuf, *pPos, *pRetBuf;
-
- if (nPreLen < 1)
- return (nil);
-
- pStart = (char*) pFullHeader;
-
- // Find first 'line' which matches prefix
- while ((pStart) && (strnicmp(pStart, pLinePrefix, nPreLen) != 0)) {
- pStart = strstr(pStart, kNewline);
- if (pStart)
- pStart += kNewlineLen;
- }
- if (!pStart)
- return nil; // Not found
-
- // Find the end of this header line
- for (pEnd = strstr(pStart, kNewline); (pEnd != nil); pEnd = strstr(pEnd, kNewline)) {
- pPos = pEnd + kNewlineLen;
- if ((*pPos == ' ') || (*pPos == '\t')) // Does header line continue on next line?
- pEnd = pPos;
- else
- break;
- }
-
- // If we ran off the end of the string, then the end is the last char in the string
- if (pEnd == nil)
- pEnd = strchr(pStart, '\0');
-
- pBuf = pRetBuf = NewPtr(pEnd - pStart + 1); // Max length of output string
- for (pPos = pStart; pPos < pEnd; pPos++) {
- if (strncmp(pPos, kNewline, kNewlineLen) == 0) // We're at a newline
- pPos += kNewlineLen; // Skip over newline
- *pBuf++ = *pPos;
- }
- *pBuf = '\0';
-
- return pRetBuf;
- }
-